/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 2.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Random shader tests.
 *//*--------------------------------------------------------------------*/

#include "es2fRandomShaderTests.hpp"
#include "glsRandomShaderCase.hpp"
#include "deStringUtil.hpp"

namespace deqp
{
namespace gles2
{
namespace Functional
{

namespace
{

gls::RandomShaderCase* createRandomShaderCase (Context& context, const char* description, const rsg::ProgramParameters& baseParams, deUint32 seed, bool vertex, bool fragment)
{
	rsg::ProgramParameters params = baseParams;

	params.seed							= seed;
	params.vertexParameters.randomize	= vertex;
	params.fragmentParameters.randomize	= fragment;

	return new gls::RandomShaderCase(context.getTestContext(), context.getRenderContext(), de::toString(seed).c_str(), description, params);
}

class BasicExpressionGroup : public TestCaseGroup
{
public:
	BasicExpressionGroup (Context& context)
		: TestCaseGroup(context, "basic_expression", "Basic arithmetic expressions")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		tcu::TestCaseGroup* combinedGroup = new tcu::TestCaseGroup(m_testCtx, "combined", "Combined tests");
		addChild(combinedGroup);

		for (int seed = 0; seed < 100; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Random expressions in vertex shader",					params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Random expressions in fragment shader",				params, seed, false, true));
			combinedGroup->addChild(createRandomShaderCase(m_context,	"Random expressions in vertex and fragment shaders",	params, seed, true, true));
		}
	}
};

class ScalarConversionGroup : public TestCaseGroup
{
public:
	ScalarConversionGroup (Context& context)
		: TestCaseGroup(context, "scalar_conversion", "Scalar conversions")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions = true;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		tcu::TestCaseGroup* combinedGroup = new tcu::TestCaseGroup(m_testCtx, "combined", "Combined tests");
		addChild(combinedGroup);

		for (int seed = 0; seed < 100; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Scalar conversions in vertex shader",					params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Scalar conversions in fragment shader",				params, seed, false, true));
			combinedGroup->addChild(createRandomShaderCase(m_context,	"Scalar conversions in vertex and fragment shaders",	params, seed, true, true));
		}
	}
};

class SwizzleGroup : public TestCaseGroup
{
public:
	SwizzleGroup (Context& context)
		: TestCaseGroup(context, "swizzle", "Vector swizzles")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions = true;
		params.useSwizzle			= true;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		for (int seed = 0; seed < 50; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Vector swizzles in vertex shader",		params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Vector swizzles in fragment shader",	params, seed, false, true));
		}
	}
};

class ComparisonOpsGroup : public TestCaseGroup
{
public:
	ComparisonOpsGroup (Context& context)
		: TestCaseGroup(context, "comparison_ops", "Comparison operators")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions = true;
		params.useComparisonOps		= true;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		for (int seed = 0; seed < 50; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Comparison operators in vertex shader",		params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Comparison operators in fragment shader",		params, seed, false, true));
		}
	}
};

class ConditionalsGroup : public TestCaseGroup
{
public:
	ConditionalsGroup (Context& context)
		: TestCaseGroup(context, "conditionals", "Conditional control flow (if-else)")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions = true;
		params.useSwizzle			= true;
		params.useComparisonOps		= true;
		params.useConditionals		= true;
		params.vertexParameters.maxStatementDepth		= 4;
		params.vertexParameters.maxStatementsPerBlock	= 5;
		params.fragmentParameters.maxStatementDepth		= 4;
		params.fragmentParameters.maxStatementsPerBlock	= 5;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		tcu::TestCaseGroup* combinedGroup = new tcu::TestCaseGroup(m_testCtx, "combined", "Combined tests");
		addChild(combinedGroup);

		for (int seed = 0; seed < 100; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Conditional control flow in vertex shader",				params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Conditional control flow in fragment shader",				params, seed, false, true));
			combinedGroup->addChild(createRandomShaderCase(m_context,	"Conditional control flow in vertex and fragment shaders",	params, seed, true, true));
		}
	}
};

class TrigonometricGroup : public TestCaseGroup
{
public:
	TrigonometricGroup (Context& context)
		: TestCaseGroup(context, "trigonometric", "Trigonometric built-in functions")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions		= true;
		params.useSwizzle				= true;
		params.trigonometricBaseWeight	= 4.0f;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		for (int seed = 0; seed < 100; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Trigonometric ops in vertex shader",	params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Trigonometric ops in fragment shader",	params, seed, false, true));
		}
	}
};

class ExponentialGroup : public TestCaseGroup
{
public:
	ExponentialGroup (Context& context)
		: TestCaseGroup(context, "exponential", "Exponential built-in functions")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions		= true;
		params.useSwizzle				= true;
		params.exponentialBaseWeight	= 4.0f;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		for (int seed = 0; seed < 100; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Exponential ops in vertex shader",		params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Exponential ops in fragment shader",	params, seed, false, true));
		}
	}
};

class TextureGroup : public TestCaseGroup
{
public:
	TextureGroup (Context& context)
		: TestCaseGroup(context, "texture", "Texture lookups")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions						= true;
		params.useSwizzle								= true;
		params.vertexParameters.texLookupBaseWeight		= 10.0f;
		params.vertexParameters.useTexture2D			= true;
		params.vertexParameters.useTextureCube			= true;
		params.fragmentParameters.texLookupBaseWeight	= 10.0f;
		params.fragmentParameters.useTexture2D			= true;
		params.fragmentParameters.useTextureCube		= true;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		// Do only 50 vertex cases and 150 fragment cases.
		for (int seed = 0; seed < 50; seed++)
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Texture lookups in vertex shader",		params, seed, true, false));

		for (int seed = 0; seed < 150; seed++)
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Texture lookups in fragment shader",	params, seed, false, true));
	}
};

class AllFeaturesGroup : public TestCaseGroup
{
public:
	AllFeaturesGroup (Context& context)
		: TestCaseGroup(context, "all_features", "All features enabled")
	{
	}

	void init (void)
	{
		rsg::ProgramParameters params;
		params.useScalarConversions		= true;
		params.useSwizzle				= true;
		params.useComparisonOps			= true;
		params.useConditionals			= true;
		params.trigonometricBaseWeight	= 1.0f;
		params.exponentialBaseWeight	= 1.0f;

		params.vertexParameters.maxStatementDepth				= 4;
		params.vertexParameters.maxStatementsPerBlock			= 7;
		params.vertexParameters.maxExpressionDepth				= 7;
		params.vertexParameters.maxCombinedVariableScalars		= 64;
		params.fragmentParameters.maxStatementDepth				= 4;
		params.fragmentParameters.maxStatementsPerBlock			= 7;
		params.fragmentParameters.maxExpressionDepth			= 7;
		params.fragmentParameters.maxCombinedVariableScalars	= 64;

		params.fragmentParameters.texLookupBaseWeight		= 4.0f; // \note Texture lookups are enabled for fragment shaders only.
		params.fragmentParameters.useTexture2D				= true;
		params.fragmentParameters.useTextureCube			= true;

		tcu::TestCaseGroup* vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Vertex-only tests");
		addChild(vertexGroup);

		tcu::TestCaseGroup* fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Fragment-only tests");
		addChild(fragmentGroup);

		for (int seed = 0; seed < 100; seed++)
		{
			vertexGroup->addChild(createRandomShaderCase(m_context,		"Texture lookups in vertex shader",		params, seed, true, false));
			fragmentGroup->addChild(createRandomShaderCase(m_context,	"Texture lookups in fragment shader",	params, seed, false, true));
		}
	}
};

} // anonymous

RandomShaderTests::RandomShaderTests (Context& context)
	: TestCaseGroup(context, "random", "Random shaders")
{
}

RandomShaderTests::~RandomShaderTests (void)
{
}

namespace
{

} // anonymous

void RandomShaderTests::init (void)
{
	addChild(new BasicExpressionGroup	(m_context));
	addChild(new ScalarConversionGroup	(m_context));
	addChild(new SwizzleGroup			(m_context));
	addChild(new ComparisonOpsGroup		(m_context));
	addChild(new ConditionalsGroup		(m_context));
	addChild(new TrigonometricGroup		(m_context));
	addChild(new ExponentialGroup		(m_context));
	addChild(new TextureGroup			(m_context));
	addChild(new AllFeaturesGroup		(m_context));
}

} // Functional
} // gles2
} // deqp
